package log

import (
	"errors"
	"fmt"
	"gopkg.in/natefinch/lumberjack.v2"
	"io"
	"log"
	"os"
	"path"
	"runtime"
	"sync"
)

const maxSub = 32
const maxLastLogs = 100
const logName = "cloud-syncer-app.log"

type Logger struct {
	subscribers []*func(m string)
	outMu       *sync.Mutex
	subMu       *sync.Mutex
	logger      *log.Logger
	maxSub      int
	lastLogs    []string
}

func NewLogger() *Logger {
	r, w := io.Pipe()
	logger := log.New(io.MultiWriter(os.Stdout, w, &lumberjack.Logger{
		Filename:   logName,
		MaxSize:    20,
		MaxBackups: 7,
		MaxAge:     28,
		Compress:   true,
	}), "", log.Ldate|log.Ltime|log.Lmsgprefix)
	l := &Logger{
		subscribers: make([]*func(m string), 0),
		outMu:       &sync.Mutex{},
		subMu:       &sync.Mutex{},
		logger:      logger,
		maxSub:      maxSub,
	}
	go func() {
		buf := make([]byte, 10240)
		for {
			n, err := r.Read(buf)
			if err != nil {
				return
			}
			message := string(buf[:n])
			l.publish(message)
		}
	}()
	return l
}

func (l *Logger) output(level string, format string, args ...any) {
	l.outMu.Lock()
	defer l.outMu.Unlock()
	_, file, line, ok := runtime.Caller(2)
	if ok {
		l.logger.SetPrefix(fmt.Sprintf("[%s] %s:%d ", level, path.Base(file), line))
	} else {
		l.logger.SetPrefix(fmt.Sprintf("[%s] ", level))
	}
	l.logger.Printf(format, args...)
}

func (l *Logger) Info(format string, args ...any) {
	l.output("INFO", format, args...)
}

func (l *Logger) Error(format string, args ...any) {
	l.output("ERROR", format, args...)
}

func (l *Logger) Debug(format string, args ...any) {
	l.output("DEBUG", format, args...)
}

func (l *Logger) Warn(format string, args ...any) {
	l.output("WARN", format, args...)
}

func (l *Logger) publish(message string) {
	l.subMu.Lock()
	defer l.subMu.Unlock()
	if len(l.lastLogs) > maxLastLogs {
		l.lastLogs = l.lastLogs[1:]
	}
	l.lastLogs = append(l.lastLogs, message)
	// 将日志发送给订阅者
	for _, subscriber := range l.subscribers {
		f := *subscriber
		f(message)
	}
}

func (l *Logger) GetLastLogs() []string {
	return l.lastLogs
}

func (l *Logger) Subscribe(subscriber *func(m string)) error {
	l.subMu.Lock()
	defer l.subMu.Unlock()
	if len(l.subscribers) >= l.maxSub {
		return errors.New("too many log subscribers")
	}
	l.subscribers = append(l.subscribers, subscriber)
	return nil
}

func (l *Logger) Unsubscribe(subscriber *func(m string)) {
	l.subMu.Lock()
	defer l.subMu.Unlock()
	subscribers := make([]*func(m string), 0)
	for _, v := range l.subscribers {
		if fmt.Sprintf("%p", v) != fmt.Sprintf("%p", subscriber) {
			subscribers = append(subscribers, v)
		}
	}
	l.subscribers = subscribers
}
